package org.kth.dks.dks_dht;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.kth.dks.dks_node.Interval;
import org.kth.dks.dks_node.IntervalOptimizer;
import org.kth.dks.util.MathMiscConstant;
class FailedIntervalHandler
    extends IntervalOptimizer
    implements Runnable {

    Map intervals;
    MathMiscConstant _math;
    int f_factor;
    long timeout;
    int ctr;
    FailedIntervalCallbackInterface dht;
    Thread worker;


    // An internal class used to maintain information regarding
    // the different intervals that are currently being restored.
    class FailedInterval{
	public int f;
	public List interval;
	public long timeout;
	public int items; 

	public FailedInterval(Interval i, long t){
	    f=0;
	    interval = new LinkedList();
	    timeout = t;
	    items = 0; 
	    interval.add(i);
	}

	public int getCC(){
	    return f + 2;
	}

	public void incCC(){
	    f = (f + 1) % (f_factor);

	}

    }


    public FailedIntervalHandler(FailedIntervalCallbackInterface d, MathMiscConstant math, int f, long t){
	super(math);
	dht = d;
	_math = math;
	intervals = new HashMap();
	f_factor = f -1;
	timeout = t;
	ctr = 0;
	worker = new Thread(this);
	worker.setName(FailedIntervalHandler.class.getName());
	worker.start();
    }



    long now() { return new java.util.Date().getTime(); }

    public void run(){
	handleIntervals();
    }

    private synchronized void handleIntervals(){
	while(true){
	    long now = now();
	    long waitTime = -1;
	    for (Iterator entryIter = intervals.entrySet().iterator(); entryIter.hasNext(); ){
		Map.Entry entry = (Map.Entry) entryIter.next();
		FailedInterval fi = (FailedInterval) entry.getValue();
		waitTime = waitTime<fi.timeout ? fi.timeout : waitTime;
	    }
	    try{
		if (waitTime == -1)
		    this.wait();
		else
		    this.wait(waitTime - now);
	    }catch(InterruptedException e){
		;
	    }


	    now = now();
	    for (Iterator entryIter = intervals.entrySet().iterator(); entryIter.hasNext(); ){
		Map.Entry entry = (Map.Entry) entryIter.next();
		FailedInterval fi = (FailedInterval) entry.getValue();
		if (fi.timeout <= now){
		    fi.incCC();
		    fi.timeout = now + timeout;
		    int id = ((Integer) entry.getKey()).intValue();
		    dht.sendRestoreIntervals(fi.interval, fi.getCC(), id);
		}
	    }
	}
    }


    // adds a new failed interval to the interval handler.
    // The method will do an automatic upcall to the
    // DHT to initialize a restore sweep over the DHT.
    public synchronized void intervalFailed(Interval in){
	int id = ctr++;
	FailedInterval fi = new FailedInterval(in, now() + timeout);
	intervals.put(new Integer(id), fi);
	dht.sendRestoreIntervals(fi.interval,fi.getCC(), id);
	this.notifyAll();
    }



    // Called when an interval of data is received
    // The call removes the passed interval from the
    // outstanding interval in the handler.
    // The method returns a list of intervals defining
    // what data in the received batch that should be
    // inserted in the store.
    public synchronized  int intervalRecieved(int id, Interval in, int items){
	FailedInterval fi = (FailedInterval) intervals.get(new Integer(id));
	if (fi == null) {
	    return -1;
	}
	fi.interval = 	removeInterval(fi.interval, in);
	fi.items += items; 
	if (fi.interval.isEmpty()) {
	    intervals.remove(new Integer(id));
	    return fi.items; 
	}
	return -1;
    }

    // checks wether an identifier is within an interval
    // that is currently being handled by the handler

    public synchronized boolean inFailedInterval(long identifier){
	for (Iterator entryIter = intervals.entrySet().iterator(); entryIter.hasNext(); ){
	    Map.Entry entry = (Map.Entry) entryIter.next();
	    FailedInterval fi = (FailedInterval) entry.getValue();
	    for (Iterator it=fi.interval.iterator(); it.hasNext();) {
		Interval iv = (Interval) it.next();
		if  (_math.belongsTo(identifier, iv.start, iv.end))
		    return true;
	    }
	}
	return false;
    }


    // finds the intersection between an interval and the intervals stored in the handler
    // takes an interval as argument and returns a list of intervals that are the intersection(s)
    // between the interval and the intervals found in the handler.




    public synchronized List intervalIntersection(Interval in){
	List li = new LinkedList();
	li.add(in);
	List differance = intervalDifference(in);
	for (Iterator it=differance.iterator(); it.hasNext();) {
	    li = removeInterval(li,(Interval) it.next());
	}
	return li;
    }

    // finds the difference between an interval and the intervals stored in the handler
    // takes an interval as argument and returns a list of intervals that are the difference(s)
    // between the interval and the intervals found in the handler.

    public synchronized List intervalDifference(Interval in){
	List ans = new LinkedList();
	ans.add(in);
	for (Iterator entryIter = intervals.entrySet().iterator(); entryIter.hasNext(); ){
	    Map.Entry entry = (Map.Entry) entryIter.next();
	    FailedInterval fi = (FailedInterval) entry.getValue();
	    List tmp = new LinkedList();
	    for (Iterator it2=fi.interval.iterator(); it2.hasNext();) {
		Interval failed = (Interval) it2.next();
		ans = removeInterval(ans, failed);
	    }
	}
	return ans;
    }

    public synchronized void removeInterval(Interval in){
	for (Iterator entryIter = intervals.entrySet().iterator(); entryIter.hasNext(); ){
	    Map.Entry entry = (Map.Entry) entryIter.next();
	    FailedInterval fi = (FailedInterval) entry.getValue();
	    fi.interval = removeInterval(fi.interval, in);
	    if (fi.interval.isEmpty())
		entryIter.remove();
	}
    }

    public synchronized boolean isEmpty(){
	return intervals.isEmpty();
    }



}
